using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;

namespace XMaze3D
{
    /// <summary>
    /// This is the main type for your game
    /// </summary>
    public class Game1 : Microsoft.Xna.Framework.Game
    {
        private const int wallsPerCell = 5;
        private const int mazeRows = 10;
        private const int mazeCols = 10;

        GraphicsDeviceManager graphics;
        SpriteBatch spriteBatch;
        Matrix viewMatrix;
        Matrix projectionMatrix;

        VertexPositionNormalTexture[] floorverts, northverts, southverts, eastverts, westverts;

        private int[] vertexCount;
        Maze theMaze;

        Vector3 playerPos, enemyPos, playerAngle, oldPlayerPos;

        Vector3 cameraReference = new Vector3(0, 0, -1);
        float viewAngle = MathHelper.PiOver4;
        const float nearClip = 0.05f;
        const float farClip = 100f;
        float aspectRatio;

        MazeCell currCell, prevCell;

        Texture2D floorTexture, northTexture, southTexture, eastTexture, westTexture;

        GamePadState p1, p1Prev;
        KeyboardState kb, kbPrev;

        BasicEffect effect;

        float daylight = 0.5f;
        bool fog = false;

        Model model;

        Random rand;

        int elapsedTime = 0;

        //Sounds
        SoundEffect footsteps;
        SoundEffectInstance footstepsInstance;

        SoundEffect scrape;
        SoundEffectInstance scrapeInstance;

        SoundEffect strike;

        Song lightMusic;
        Song darkMusic;

        float Volume = 1.0f;

        bool collide = false;
        bool prevCollide = false;
        bool stuck = false;

        Direction wallCollide = (Direction)(-1);
        Direction prevWallCollide = (Direction)(-1);

        //Toggles
        private bool noClip;

        private bool bgMusic = true;

        public Game1()
        {
            graphics = new GraphicsDeviceManager(this);
            Content.RootDirectory = "Content";

            theMaze = new Maze(mazeRows, mazeCols);

            floorverts = new VertexPositionNormalTexture[mazeRows * mazeCols * wallsPerCell * 4];
            northverts = new VertexPositionNormalTexture[mazeRows * mazeCols * wallsPerCell * 4];
            southverts = new VertexPositionNormalTexture[mazeRows * mazeCols * wallsPerCell * 4];
            eastverts = new VertexPositionNormalTexture[mazeRows * mazeCols * wallsPerCell * 4];
            westverts = new VertexPositionNormalTexture[mazeRows * mazeCols * wallsPerCell * 4];

            vertexCount = new int[wallsPerCell];

            rand = new Random();

            SetDefaults();
        }

        private void SetDefaults()
        {
            playerPos = new Vector3(0, 0, 5f);
            enemyPos = Vector3.Zero;
            playerAngle = Vector3.Zero;
            noClip = false;
        }

        /// <summary>
        /// Allows the game to perform any initialization it needs to before starting to run.
        /// This is where it can query for any required services and load any non-graphic
        /// related content.  Calling base.Initialize will enumerate through any components
        /// and initialize them as well.
        /// </summary>
        protected override void Initialize()
        {
            aspectRatio = graphics.GraphicsDevice.Viewport.Width / graphics.GraphicsDevice.Viewport.Height;

            effect = new BasicEffect(graphics.GraphicsDevice);

            IntializeMaze();

            MediaPlayer.IsRepeating = true;

            base.Initialize();
        }

        private void IntializeMaze()
        {
            vertexCount = new int[] { 0, 0, 0, 0, 0 };

            Vector3 normalTop = new Vector3(0.0f, 1.0f, 0.0f);
            Vector3 normalNorth = new Vector3(0.0f, 0.0f, -1.0f);
            Vector3 normalSouth = new Vector3(0.0f, 0.0f, 1.0f);
            Vector3 normalEast = new Vector3(-1.0f, 0.0f, 0.0f);
            Vector3 normalWest = new Vector3(1.0f, 0.0f, 0.0f);

            Vector2 textureTopLeft = new Vector2(1.0f, 0.0f);
            Vector2 textureTopRight = new Vector2(0.0f, 0.0f);
            Vector2 textureBottomLeft = new Vector2(1.0f, 1.0f);
            Vector2 textureBottomRight = new Vector2(0.0f, 1.0f);

            theMaze.Create();
            for (int i = 0; i < mazeRows; i++)
            {
                for (int j = 0; j < mazeCols; j++)
                {
                    if (theMaze.GetCell(i, j).northWallPresent)
                    {
                        northverts[vertexCount[(int)Direction.dirNORTH]++] = new VertexPositionNormalTexture(new Vector3(-0.5f + j, -0.5f, 0.5f - i), normalNorth, textureBottomLeft);
                        northverts[vertexCount[(int)Direction.dirNORTH]++] = new VertexPositionNormalTexture(new Vector3(-0.5f + j, 0.5f, 0.5f - i), normalNorth, textureTopLeft);
                        northverts[vertexCount[(int)Direction.dirNORTH]++] = new VertexPositionNormalTexture(new Vector3(0.5f + j, 0.5f, 0.5f - i), normalNorth, textureTopRight);
                        northverts[vertexCount[(int)Direction.dirNORTH]++] = new VertexPositionNormalTexture(new Vector3(-0.5f + j, -0.5f, 0.5f - i), normalNorth, textureBottomLeft);
                        northverts[vertexCount[(int)Direction.dirNORTH]++] = new VertexPositionNormalTexture(new Vector3(0.5f + j, 0.5f, 0.5f - i), normalNorth, textureTopRight);
                        northverts[vertexCount[(int)Direction.dirNORTH]++] = new VertexPositionNormalTexture(new Vector3(0.5f + j, -0.5f, 0.5f - i), normalNorth, textureBottomRight);
                    }
                    if (theMaze.GetCell(i, j).southWallPresent)
                    {
                        southverts[vertexCount[(int)Direction.dirNORTH]++] = new VertexPositionNormalTexture(new Vector3(-0.4999f + j, -0.5f, -0.4999f - i), normalSouth, textureBottomRight);
                        southverts[vertexCount[(int)Direction.dirNORTH]++] = new VertexPositionNormalTexture(new Vector3(-0.4999f + j, 0.5f, -0.4999f - i), normalSouth, textureTopRight);
                        southverts[vertexCount[(int)Direction.dirNORTH]++] = new VertexPositionNormalTexture(new Vector3(0.4999f + j, 0.5f, -0.4999f - i), normalSouth, textureTopLeft);
                        southverts[vertexCount[(int)Direction.dirNORTH]++] = new VertexPositionNormalTexture(new Vector3(-0.4999f + j, -0.5f, -0.4999f - i), normalSouth, textureBottomRight);
                        southverts[vertexCount[(int)Direction.dirNORTH]++] = new VertexPositionNormalTexture(new Vector3(0.4999f + j, 0.5f, -0.4999f - i), normalSouth, textureTopLeft);
                        southverts[vertexCount[(int)Direction.dirNORTH]++] = new VertexPositionNormalTexture(new Vector3(0.4999f + j, -0.5f, -0.4999f - i), normalSouth, textureBottomLeft);
                    }
                    if (theMaze.GetCell(i, j).eastWallPresent)
                    {
                        eastverts[vertexCount[(int)Direction.dirNORTH]++] = new VertexPositionNormalTexture(new Vector3(0.5f + j, -0.5f, -0.5f - i), normalEast, textureBottomRight);
                        eastverts[vertexCount[(int)Direction.dirNORTH]++] = new VertexPositionNormalTexture(new Vector3(0.5f + j, 0.5f, -0.5f - i), normalEast, textureTopRight);
                        eastverts[vertexCount[(int)Direction.dirNORTH]++] = new VertexPositionNormalTexture(new Vector3(0.5f + j, 0.5f, 0.5f - i), normalEast, textureTopLeft);
                        eastverts[vertexCount[(int)Direction.dirNORTH]++] = new VertexPositionNormalTexture(new Vector3(0.5f + j, -0.5f, -0.5f - i), normalEast, textureBottomRight);
                        eastverts[vertexCount[(int)Direction.dirNORTH]++] = new VertexPositionNormalTexture(new Vector3(0.5f + j, 0.5f, 0.5f - i), normalEast, textureTopLeft);
                        eastverts[vertexCount[(int)Direction.dirNORTH]++] = new VertexPositionNormalTexture(new Vector3(0.5f + j, -0.5f, 0.5f - i), normalEast, textureBottomLeft);
                    }
                    if (theMaze.GetCell(i, j).westWallPresent)
                    {
                        westverts[vertexCount[(int)Direction.dirNORTH]++] = new VertexPositionNormalTexture(new Vector3(-0.4999f + j, -0.5f, -0.4999f - i), normalWest, textureTopRight);
                        westverts[vertexCount[(int)Direction.dirNORTH]++] = new VertexPositionNormalTexture(new Vector3(-0.4999f + j, 0.5f, -0.4999f - i), normalWest, textureBottomRight);
                        westverts[vertexCount[(int)Direction.dirNORTH]++] = new VertexPositionNormalTexture(new Vector3(-0.4999f + j, 0.5f, 0.4999f - i), normalWest, textureBottomLeft);
                        westverts[vertexCount[(int)Direction.dirNORTH]++] = new VertexPositionNormalTexture(new Vector3(-0.4999f + j, -0.5f, -0.4999f - i), normalWest, textureTopRight);
                        westverts[vertexCount[(int)Direction.dirNORTH]++] = new VertexPositionNormalTexture(new Vector3(-0.4999f + j, 0.5f, 0.4999f - i), normalWest, textureBottomLeft);
                        westverts[vertexCount[(int)Direction.dirNORTH]++] = new VertexPositionNormalTexture(new Vector3(-0.4999f + j, -0.5f, 0.4999f - i), normalWest, textureTopLeft);
                    }

                    floorverts[vertexCount[(int)Direction.FLOOR]++] = new VertexPositionNormalTexture(new Vector3(-0.5f + j, -0.5f, -0.5f - i), normalTop, textureBottomLeft);
                    floorverts[vertexCount[(int)Direction.FLOOR]++] = new VertexPositionNormalTexture(new Vector3(0.5f + j, -0.5f, -0.5f - i), normalTop, textureBottomRight);
                    floorverts[vertexCount[(int)Direction.FLOOR]++] = new VertexPositionNormalTexture(new Vector3(0.5f + j, -0.5f, 0.5f - i), normalTop, textureTopRight);
                    floorverts[vertexCount[(int)Direction.FLOOR]++] = new VertexPositionNormalTexture(new Vector3(-0.5f + j, -0.5f, -0.5f - i), normalTop, textureBottomLeft);
                    floorverts[vertexCount[(int)Direction.FLOOR]++] = new VertexPositionNormalTexture(new Vector3(0.5f + j, -0.5f, 0.5f - i), normalTop, textureTopRight);
                    floorverts[vertexCount[(int)Direction.FLOOR]++] = new VertexPositionNormalTexture(new Vector3(-0.5f + j, -0.5f, 0.5f - i), normalTop, textureTopLeft);
                }
            }
        }

        /// <summary>
        /// LoadContent will be called once per game and is the place to load
        /// all of your content.
        /// </summary>
        protected override void LoadContent()
        {
            // Create a new SpriteBatch, which can be used to draw textures.
            spriteBatch = new SpriteBatch(GraphicsDevice);

            floorTexture = Content.Load<Texture2D>("Textures/Floor");
            northTexture = Content.Load<Texture2D>("Textures/NorthWall");
            southTexture = Content.Load<Texture2D>("Textures/SouthWall");
            westTexture = Content.Load<Texture2D>("Textures/WestWall");
            eastTexture = Content.Load<Texture2D>("Textures/EastWall");

            model = Content.Load<Model>("SpaceShip");

            footsteps = Content.Load<SoundEffect>("Sounds/footsteps");
            footstepsInstance = footsteps.CreateInstance();

            scrape = Content.Load<SoundEffect>("Sounds/scrape");
            scrapeInstance = scrape.CreateInstance();

            strike = Content.Load<SoundEffect>("Sounds/strike");

            lightMusic = Content.Load<Song>("Music/lightMusic");
            darkMusic = Content.Load<Song>("Music/darkMusic");

            List<Song> songs = new List<Song>();

            songs.Add(lightMusic);
            songs.Add(darkMusic);
        }

        /// <summary>
        /// UnloadContent will be called once per game and is the place to unload
        /// all content.
        /// </summary>
        protected override void UnloadContent()
        {
            // TODO: Unload any non ContentManager content here
        }

        /// <summary>
        /// Allows the game to run logic such as updating the world,
        /// checking for collisions, gathering input, and playing audio.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Update(GameTime gameTime)
        {
            // Allows the game to exit
            if ((GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed) ||
                (Keyboard.GetState(PlayerIndex.One).IsKeyDown(Keys.Escape)))
                this.Exit();

            p1 = GamePad.GetState(PlayerIndex.One);
            kb = Keyboard.GetState();

            UpdateEffects();
            MoveEnemy(gameTime);
            UpdatePlayerPosition();
            UpdateInput();

            if (!noClip)
            {
                UpdateCollision();
            }
            else
            {
                //Reset collision type detection variables
                collide = false;
                prevCollide = false;
                stuck = false;
                scrapeInstance.Pause();
            }

            PlayCollisionSound();

            UpdateCamera();

            //Play daytime or nighttime music
            if (daylight >= 0.5f && 
                (MediaPlayer.State != MediaState.Playing||
                MediaPlayer.Queue.ActiveSong != lightMusic)
                && bgMusic)
                MediaPlayer.Play(lightMusic);
            else if (daylight < 0.5f && 
                (MediaPlayer.State != MediaState.Playing || 
                MediaPlayer.Queue.ActiveSong != darkMusic )
                && bgMusic)
                MediaPlayer.Play(darkMusic);

            //Adjust volume based on enemy proximity
            Volume = 1 - (playerPos - enemyPos).Length() / 20.0f;

            //Adjust volume based on fog
            Volume = Volume * (fog ? 0.5f : 1.0f);

            //Apply volume to background music
            MediaPlayer.Volume = Volume;

            p1Prev = p1;
            kbPrev = kb;
            base.Update(gameTime);
        }

        void UpdateCollision()
        {
            int test;

            int currRow = (int)Math.Round(-playerPos.Z);
            int currCol = (int)Math.Round(playerPos.X);

            float cellPosX = playerPos.X % 1;
            float cellPosZ = playerPos.Z % 1;

            currCell = theMaze.GetCell(currRow, currCol);
            MazeCell nextCell = null;

            prevCollide = collide;
            prevWallCollide = wallCollide;
            collide = false;

            if (p1.IsButtonDown(Buttons.A))
                test = 1;
            if (p1.IsButtonDown(Buttons.B))
                cellPosZ = -0.49f;


            if ((cellPosZ > 0.5 && cellPosZ <= 0.6) || (cellPosZ > -0.5 && cellPosZ <= -0.4))
            {
                nextCell = theMaze.GetCell(currRow + 1, currCol);
                if ((currCell != null && currCell.southWallPresent) || (nextCell != null && nextCell.northWallPresent))
                {
                    playerPos.Z = -currRow - 0.4f;
                    collide = true;
                    wallCollide = Direction.dirSOUTH;
                }
            }
            if ((cellPosZ < 0.5 && cellPosZ >= 0.4) || (cellPosZ < -0.5 && cellPosZ >= -0.6))
            {
                nextCell = theMaze.GetCell(currRow - 1, currCol);
                if ((currCell != null && currCell.northWallPresent) || (nextCell != null && nextCell.southWallPresent))
                {
                    playerPos.Z = -currRow + 0.4f;
                    collide = true;
                    wallCollide = Direction.dirNORTH;
                }
            }
            if ((cellPosX > 0.5 && cellPosX <= 0.6) || (cellPosX > -0.5 && cellPosX <= -0.4))
            {
                nextCell = theMaze.GetCell(currRow, currCol - 1);
                if ((currCell != null && currCell.westWallPresent) || (nextCell != null && nextCell.eastWallPresent))
                {
                    playerPos.X = currCol - 0.4f;
                    collide = true;
                    wallCollide = Direction.dirWEST;
                }
            }
            if ((cellPosX < 0.5 && cellPosX >= 0.4) || (cellPosX < -0.5 && cellPosX >= -0.6))
            {
                nextCell = theMaze.GetCell(currRow, currCol + 1);
                if ((currCell != null && currCell.eastWallPresent) || (nextCell != null && nextCell.westWallPresent))
                {
                    playerPos.X = currCol + 0.4f;
                    wallCollide = Direction.dirEAST;
                    collide = true;
                }
            }
            if (currRow == -1 && nextCell != null)
                test = 2;

            prevCell = currCell;
        }

        //Play sounds depending on the type of collision - momentary or prolonged
        void PlayCollisionSound()
        {
            if (oldPlayerPos.X != playerPos.X || oldPlayerPos.Z != playerPos.Z)
            {
                //Creepy footsteps
                footstepsInstance.Play();
                //Check for scraping against wall
                if (prevCollide)
                {
                    scrapeInstance.Play();
                    stuck = true;
                }
                else
                    scrapeInstance.Pause();
                //Initial hit agaist the wall
                if ((!prevCollide && collide) || prevWallCollide != wallCollide)
                    strike.Play();
            }
            else
            {
                footstepsInstance.Pause();
                scrapeInstance.Pause();

                if (prevCollide && collide && stuck)
                {
                    stuck = false;
                    strike.Play();
                }
            }
        }

        // Randomly pick a direction and update the enemy model position
        public void MoveEnemy(GameTime gameTime)
        {
            elapsedTime += gameTime.ElapsedGameTime.Milliseconds;
            int currRow = (int)-enemyPos.Z;
            int currCol = (int)enemyPos.X;

            MazeCell cell = theMaze.GetCell(currRow, currCol);

            int direction = rand.Next(0, 4);

            if (elapsedTime >= 500)
            {
                switch (direction)
                {
                    case 0:
                        if (currRow > 0 && !cell.northWallPresent)
                            enemyPos.Z++;
                        break;
                    case 1:
                        if (currRow < theMaze.rows - 1 && !cell.southWallPresent)
                            enemyPos.Z--;
                        break;
                    case 2:
                        if (currCol > 0 && !cell.westWallPresent)
                            enemyPos.X--;
                        break;
                    case 3:
                        if (currCol < theMaze.cols - 1 && !cell.eastWallPresent)
                            enemyPos.X++;
                        break;
                }

                elapsedTime = 0;
            }
        }

        void UpdateInput()
        {
            // Reset
            if (p1.IsButtonDown(Buttons.Start) || (kb.IsKeyDown(Keys.Home) && kbPrev.IsKeyUp(Keys.Home)))
            {
                SetDefaults();
            }

            // Tun on/off clip
            if ((p1.IsButtonDown(Buttons.Y) && p1Prev.IsButtonUp(Buttons.Y)) ||
                kb.IsKeyDown(Keys.W) && kbPrev.IsKeyUp(Keys.W))
                noClip = !noClip;

            // Zoom in
            if (p1.Buttons.B == ButtonState.Pressed || (kb.IsKeyDown(Keys.Z) && kb.IsKeyUp(Keys.LeftShift)))
            {
                viewAngle -= MathHelper.ToRadians(1);
            }

            // Zoom out
            if (p1.Buttons.A == ButtonState.Pressed || (kb.IsKeyDown(Keys.Z) && kb.IsKeyDown(Keys.LeftShift)))
            {
                viewAngle += MathHelper.ToRadians(1);
            }

            // Reset zoom
            if (p1.Buttons.B != ButtonState.Pressed && p1.Buttons.A != ButtonState.Pressed && kb.IsKeyUp(Keys.Z))
            {
                viewAngle = MathHelper.PiOver4;
            }

            // Clamp viewangle to between 10 and 100;
            viewAngle = MathHelper.Clamp(viewAngle, MathHelper.ToRadians(10), MathHelper.ToRadians(100));

            // Fog control
            if (p1.Buttons.X == ButtonState.Pressed && p1Prev.Buttons.X != ButtonState.Pressed || kb.IsKeyDown(Keys.F) && kbPrev.IsKeyUp(Keys.F))
            {
                fog = !fog;
            }

            // Light intensity control
            if (p1.DPad.Down == ButtonState.Pressed || kb.IsKeyDown(Keys.OemMinus))
            {
                daylight -= 0.01f;
            }
            if (p1.DPad.Up == ButtonState.Pressed || kb.IsKeyDown(Keys.OemPlus))
            {
                daylight += 0.01f;
            }
            daylight = MathHelper.Clamp(daylight, 0.05f, 0.6f);

            //Music control
            if (p1.DPad.Right == ButtonState.Pressed || kb.IsKeyDown(Keys.M))
            {
                bgMusic = true;
            }
            if (p1.DPad.Left == ButtonState.Pressed || kb.IsKeyDown(Keys.M))
            {
                bgMusic = false;
                MediaPlayer.Pause();
            }
        }

        void UpdatePlayerPosition()
        {
            oldPlayerPos = playerPos;

            //Left-Right
            playerAngle.Y -= p1.ThumbSticks.Right.X / 20;

            //Up-Down
            playerAngle.X += p1.ThumbSticks.Right.Y / 20;
            playerAngle.X += kb.IsKeyDown(Keys.PageUp) ? 0.02f : 0;
            playerAngle.X -= kb.IsKeyDown(Keys.PageDown) ? 0.02f : 0;

            playerAngle.X = Math.Min(playerAngle.X, MathHelper.PiOver2);
            playerAngle.X = Math.Max(playerAngle.X, -MathHelper.PiOver2);

            //Forward-Backward
            Matrix forwardMovement = Matrix.CreateRotationY(playerAngle.Y);
            Vector3 v = new Vector3(0, 0, p1.ThumbSticks.Left.Y / 40);
            v.Z += kb.IsKeyDown(Keys.Up) ? 0.02f : 0;
            v.Z -= kb.IsKeyDown(Keys.Down) ? 0.02f : 0;
            v = Vector3.Transform(v, forwardMovement);
            playerPos.Z -= v.Z;
            playerPos.X -= v.X;

            //Strafe
            Matrix strafeMovement = forwardMovement * Matrix.CreateRotationY(MathHelper.PiOver2);
            v = new Vector3(0, 0, p1.ThumbSticks.Left.X / 40);
            v.Z += kb.IsKeyDown(Keys.Right) ? 0.02f : 0;
            v.Z -= kb.IsKeyDown(Keys.Left) ? 0.02f : 0;
            v = Vector3.Transform(v, strafeMovement);
            playerPos.Z += v.Z;
            playerPos.X += v.X;

            //Float Up-Down
            if (p1.IsButtonDown(Buttons.RightShoulder))
                playerPos.Y += 0.1f;
            if (p1.IsButtonDown(Buttons.LeftShoulder))
                playerPos.Y -= 0.1f;
        }

        void UpdateCamera()
        {
            Vector3 cameraPosition = playerPos;

            Matrix rotationMatrix = Matrix.CreateRotationY(playerAngle.Y);
            Vector3 transformedReference = Vector3.Transform(cameraReference, rotationMatrix);

            Matrix rotationMatrix2 = Matrix.CreateRotationX(-playerAngle.X);

            Vector3 cameraLookat = cameraPosition + transformedReference;
            viewMatrix = Matrix.CreateLookAt(cameraPosition, cameraLookat, Vector3.Up);

            viewMatrix *= rotationMatrix2;

            projectionMatrix = Matrix.CreatePerspectiveFieldOfView(viewAngle, aspectRatio, nearClip, farClip);
        }

        public void UpdateEffects()
        {
            // Set up lighting
            effect.LightingEnabled = true;
            effect.DirectionalLight0.DiffuseColor = new Vector3(daylight, daylight, daylight);
            effect.DirectionalLight0.Direction = new Vector3(0, -1, 0);
            effect.DirectionalLight0.SpecularColor = new Vector3(0.5f, 0.5f, 0.5f);
            effect.DirectionalLight0.Enabled = true;
            effect.AmbientLightColor = new Vector3(daylight, daylight, daylight);
            // Setup fog effects
            effect.FogEnabled = fog;
            effect.FogColor = Color.Gray.ToVector3();
            effect.FogStart = 0.5f;
            effect.FogEnd = 2.0f;
        }

        /// <summary>
        /// This is called when the game should draw itself.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Draw(GameTime gameTime)
        {
            if (fog)
                GraphicsDevice.Clear(Color.Gray);
            else
                GraphicsDevice.Clear(Color.CornflowerBlue * daylight);
            effect.World = Matrix.Identity;
            effect.Projection = projectionMatrix;
            effect.View = viewMatrix;
            effect.TextureEnabled = true;

            //Disable back face culling
            RasterizerState state = new RasterizerState();
            state.CullMode = CullMode.None;
            GraphicsDevice.RasterizerState = state;

            DrawWalls();
            DrawModel();


            base.Draw(gameTime);
        }

        public void DrawModel()
        {
            foreach (EffectPass pass in effect.CurrentTechnique.Passes)
            {
                Matrix[] transforms = new Matrix[model.Bones.Count];
                model.CopyAbsoluteBoneTransformsTo(transforms);

                foreach (ModelMesh mesh in model.Meshes)
                {
                    foreach (BasicEffect efx in mesh.Effects)
                    {
                        efx.EnableDefaultLighting();
                        efx.World = transforms[mesh.ParentBone.Index] *
                                    Matrix.CreateScale(0.5f) *
                                    Matrix.CreateRotationY(MathHelper.ToRadians(180)) *
                                    Matrix.CreateTranslation(enemyPos);
                        efx.View = viewMatrix;
                        efx.Projection = projectionMatrix;
                    }

                    mesh.Draw();
                }
            }
        }

        public void DrawWalls()
        {
            effect.Texture = floorTexture;
            foreach (EffectPass pass in effect.CurrentTechnique.Passes)
            {
                pass.Apply();

                using (VertexBuffer buffer = new VertexBuffer(
                    GraphicsDevice,
                    VertexPositionNormalTexture.VertexDeclaration,
                    floorverts.Length,
                    BufferUsage.WriteOnly))
                {
                    VertexPositionNormalTexture[] tempVertices = floorverts;
                    buffer.SetData(tempVertices);
                    GraphicsDevice.SetVertexBuffer(buffer);
                }
                GraphicsDevice.DrawPrimitives(PrimitiveType.TriangleList, 0, floorverts.Length / 3);
            }

            effect.Texture = northTexture;
            foreach (EffectPass pass in effect.CurrentTechnique.Passes)
            {
                pass.Apply();

                using (VertexBuffer buffer = new VertexBuffer(
                    GraphicsDevice,
                    VertexPositionNormalTexture.VertexDeclaration,
                    floorverts.Length,
                    BufferUsage.WriteOnly))
                {
                    VertexPositionNormalTexture[] tempVertices = northverts;
                    buffer.SetData(tempVertices);
                    GraphicsDevice.SetVertexBuffer(buffer);
                }
                GraphicsDevice.DrawPrimitives(PrimitiveType.TriangleList, 0, northverts.Length / 3);
            }

            effect.Texture = southTexture;
            foreach (EffectPass pass in effect.CurrentTechnique.Passes)
            {
                pass.Apply();

                using (VertexBuffer buffer = new VertexBuffer(
                    GraphicsDevice,
                    VertexPositionNormalTexture.VertexDeclaration,
                    floorverts.Length,
                    BufferUsage.WriteOnly))
                {
                    VertexPositionNormalTexture[] tempVertices = southverts;
                    buffer.SetData(tempVertices);
                    GraphicsDevice.SetVertexBuffer(buffer);
                }
                GraphicsDevice.DrawPrimitives(PrimitiveType.TriangleList, 0, southverts.Length / 3);
            }

            effect.Texture = westTexture;
            foreach (EffectPass pass in effect.CurrentTechnique.Passes)
            {
                pass.Apply();

                using (VertexBuffer buffer = new VertexBuffer(
                    GraphicsDevice,
                    VertexPositionNormalTexture.VertexDeclaration,
                    floorverts.Length,
                    BufferUsage.WriteOnly))
                {
                    VertexPositionNormalTexture[] tempVertices = eastverts;
                    buffer.SetData(tempVertices);
                    GraphicsDevice.SetVertexBuffer(buffer);
                }
                GraphicsDevice.DrawPrimitives(PrimitiveType.TriangleList, 0, eastverts.Length / 3);
            }

            effect.Texture = eastTexture;
            foreach (EffectPass pass in effect.CurrentTechnique.Passes)
            {
                pass.Apply();

                using (VertexBuffer buffer = new VertexBuffer(
                    GraphicsDevice,
                    VertexPositionNormalTexture.VertexDeclaration,
                    floorverts.Length,
                    BufferUsage.WriteOnly))
                {
                    VertexPositionNormalTexture[] tempVertices = westverts;
                    buffer.SetData(tempVertices);
                    GraphicsDevice.SetVertexBuffer(buffer);
                }
                GraphicsDevice.DrawPrimitives(PrimitiveType.TriangleList, 0, westverts.Length / 3);
            }
        }
    }
}
